/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：实现程序打包后查看输出日志
 * PC平台通过快捷键打开控制台
 * 安卓端通过晃动手机打开控制台
 CreateTime：2020-05-08 09:41:25
* Version：1.0
* Modify Recorder：
*************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;

namespace TinyToolKit
{
    public class DebugConsole : MonoBehaviour
    {
        class Log
        {
            public string time;
            public string message;
            public string stackTrace;
            public LogType logType;
            public int repeatCount;
        }

        public KeyCode shortcut = KeyCode.Tab;
        public bool shakeToOpen = true;
        public float shakeAcceleration = 3f;
        public bool isLimitLogCount = true;
        public int maxLogCount = 1000;

        private List<Log> _logs;
        private Vector2 _scrollPosition;
        private bool _visible;
        private bool _collapse;
        private bool _stackTrace;
        private bool _useLog;
        private bool _useWarning;
        private bool _useAssert;  
        private bool _useError;
        private bool _useException;  

        Dictionary<LogType, Color> _logTypeColors = new Dictionary<LogType, Color>()
        {
            {LogType.Assert, Color.white},
            {LogType.Log, Color.white},
            {LogType.Warning, Color.yellow},
            {LogType.Error, Color.red},
            {LogType.Exception, Color.red},
        };

        private float _widthScale; 
        private float _heightScale;
        private int _edge;
        private Rect _windowRect;
        private Rect _titleBarRect;

        private void Awake()
        {
            _logs = new List<Log>();
        }

        private void Start()
        {
            
            _edge = 20;
            _widthScale = 1;
            _heightScale = 1;
            _useLog = true;
            _useWarning = true;
            _useError = true;
            _useException = true;  
            _windowRect = new Rect(_edge, _edge, (Screen.width - (_edge * 2)) * _widthScale, (Screen.height - (_edge * 2)) * _heightScale);
            _titleBarRect = new Rect(0, 0, Screen.width * _widthScale, _edge);
        }

        private void OnEnable()
        {
            Application.logMessageReceivedThreaded  += HandleLog;
        }

        private void OnDisable()
        {
            Application.logMessageReceivedThreaded  -= HandleLog;
        }

        private void HandleLog(string message, string stackTrace, LogType type)
        {
            _logs.Add(new Log
            {
                time = DateTime.Now.ToString("T"),
                message = message,
                stackTrace = stackTrace,
                logType = type
            });
            
            int count = 0;
            for (int i = 0; i < _logs.Count; i++)
            {
                if (_logs[i].message == message)
                {
                    count++;
                    _logs[i].repeatCount = 0;
                }
            }

            if (count > 0)
                _logs[_logs.Count - 1].repeatCount = count;

            if (!isLimitLogCount) return;
            int removeCount = Mathf.Max(_logs.Count - maxLogCount, 0);
            if (removeCount > 0)
                _logs.RemoveRange(0, removeCount);
        }

        private void Update()
        {
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
            if (Input.GetKeyDown(shortcut))
                _visible = !_visible;
#elif UNITY_ANDROID
            if (shakeToOpen && Input.acceleration.sqrMagnitude > shakeAcceleration)
                _visible = !_visible;
#endif
        }

        private void OnGUI()
        {
            if (!_visible) return;
            
            _windowRect = GUILayout.Window(123, _windowRect, DrawConsoleWindow, "Debug Console");
            _windowRect = new Rect(_windowRect.x, _windowRect.y, (Screen.width - _edge * 2) * _widthScale, (Screen.height - (_edge * 2)) * _heightScale);
        }

        private void DrawConsoleWindow(int windowId)
        {
            _titleBarRect = new Rect(_titleBarRect.x, _titleBarRect.y, Screen.width * _widthScale, _edge);
            GUI.DragWindow(_titleBarRect);
            DrawSetting();
            DrawLogsList();
            DrawToolBar();
        }

        private void DrawSetting()
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label("Width: ",GUILayout.Width(60f));
            if (GUILayout.Button("-",GUILayout.Width(60f)))
            {
                _widthScale -= 0.1f;
                _widthScale = Mathf.Clamp01(_widthScale);
            }
            if (GUILayout.Button("+",GUILayout.Width(60f)))
            {
                _widthScale += 0.1f;
                _widthScale = Mathf.Clamp01(_widthScale);
            }
            
            GUILayout.Label("Height: ",GUILayout.Width(60f));
            if (GUILayout.Button("-",GUILayout.Width(60f)))
            {
                _heightScale -= 0.1f;
                _heightScale = Mathf.Clamp01(_heightScale);
            }
            if (GUILayout.Button("+",GUILayout.Width(60f)))
            {
                _heightScale += 0.1f;
                _heightScale = Mathf.Clamp01(_heightScale);
            }
            if (GUILayout.Button("Reset",GUILayout.Width(80f)))
            {
                _widthScale = 1;
                _heightScale = 1;
                _heightScale = Mathf.Clamp01(_heightScale);
            }
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            _useLog = GUILayout.Toggle(_useLog, "Log",GUILayout.Width(80));
            _useWarning = GUILayout.Toggle(_useWarning, "Warning",GUILayout.Width(80));
            _useAssert = GUILayout.Toggle(_useAssert, "Assert",GUILayout.Width(80)); 
            _useError = GUILayout.Toggle(_useError, "Error",GUILayout.Width(80));
            _useException = GUILayout.Toggle(_useException, "Exception",GUILayout.Width(80));
            GUILayout.EndHorizontal();
        }

        private void DrawLogsList()
        {
            _scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
            for (var i = 0; i < _logs.Count; i++)
            {
                if(!_useLog && _logs[i].logType == LogType.Log) continue;
                if(!_useWarning && _logs[i].logType == LogType.Warning) continue;
                if(!_useAssert && _logs[i].logType == LogType.Assert) continue;
                if(!_useError && _logs[i].logType == LogType.Error) continue;
                if(!_useException && _logs[i].logType == LogType.Exception) continue;
                
                string msg = "";
                if (_collapse)
                {
                    if (_logs[i].repeatCount == 0)
                        continue;
                    if (_stackTrace)
                        msg = string.Format("[{0}] {1} ({2})\n{3}", _logs[i].time, _logs[i].message,_logs[i].repeatCount,_logs[i].stackTrace);
                    else
                        msg = string.Format("[{0}] {1} ({2})", _logs[i].time, _logs[i].message, _logs[i].repeatCount);
                }
                else
                {
                    if (_stackTrace)
                        msg = string.Format("[{0}] {1}\n{2}", _logs[i].time, _logs[i].message,_logs[i].stackTrace);
                    else
                        msg = string.Format("[{0}] {1}", _logs[i].time, _logs[i].message);
                }

                if (!string.IsNullOrEmpty(msg))
                {
                    GUI.contentColor = _logTypeColors[_logs[i].logType];
                    GUILayout.Label(msg);
                    GUI.contentColor = Color.white;
                }
            }

            GUILayout.EndScrollView();
        }

        private void DrawToolBar()
        {
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Clear"))
                _logs.Clear();

            _collapse = GUILayout.Toggle(_collapse, "Collapse", GUILayout.ExpandWidth(false));
            _stackTrace = GUILayout.Toggle(_stackTrace, "StackTrace", GUILayout.ExpandWidth(false));
            GUILayout.EndHorizontal();
        }
    }
}

